import sys

import cv2
from PyQt5.QtWidgets import QMessageBox
from GUI.detect import Ui_Detect_MainWindow
from PyQt5 import QtWidgets
from PyQt5.QtGui import QImage
from PyQt5.QtCore import Qt
from PyQt5 import QtGui

from fatigue_algo.yaml_load import load_yaml
from service.detect_handler import DetectHandler

from param_setting_window.blink_param_window import BlinkParamWindow
from param_setting_window.yawn_param_window import YawnParamWindow
from param_setting_window.nod_param_window import NodParamWindow
from param_setting_window.fatigue_param_window import FatigueParamWindow

class DetectWindow(QtWidgets.QMainWindow,Ui_Detect_MainWindow):

    def __init__(self,face_detector,landmark_detector):
        super(DetectWindow, self).__init__()
        self.setupUi(self)  # 创建窗体对象
        self.scrfd_detector = face_detector
        self.landmark_detector = landmark_detector
        self.init()
        self.camera = None
        self.choices = {
            'blink' : False,
            'yawn' : False,
            'head' : False,
            'frown': False,
            'fatigue': False
        }
        self.detectWindow = self   #获取detect页面的对象，用于传递给子页面

        #加载模型配置文件参数
        self.cfg = load_yaml("blink_validate_config.yaml")
        self.checkBox_paramWindow_map = dict()   #key为checkBox的英文代号，value为指定的参数窗口

        #参数配置子页面（负责修改cfg字典对象）
        self.blinkParam_Window = None
        self.yawnParam_Window = None
        self.nodParam_Window = None
        self.frownParam_Window = None
        self.fatigueParam_Window = None

        #疲劳检测模型加载器，用于处理疲劳检测结果
        self.detectorHandler = None

    def init(self):
        # 绑定开始检测按钮事件
        self.pushButton.clicked.connect(lambda : self.fatigue_detect_on())
        # 绑定结束检测按钮事件
        self.pushButton_2.clicked.connect(lambda: self.fatigue_detect_down())

        #界面的默认配图
        img = cv2.imread('fatigue.png')
        height, width, bytesPerComponent = img.shape
        bytesPerLine = 3 * width
        cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img)

        # 利用QImage加载图片到组件中
        qImg = QImage(img.data, width, height, bytesPerLine, QImage.Format_RGB888)  # PIL image
        qImg_scaled = qImg.scaled(self.label.width(), self.label.height(), Qt.IgnoreAspectRatio,
                                  Qt.SmoothTransformation)

        pixmap = QtGui.QPixmap.fromImage(qImg_scaled)
        self.label.setPixmap(pixmap)  # 利用QPixmap组件绘制图片

    # 开始检测（如果摄像头已开启，则会弹窗提示）
    def fatigue_detect_on(self):
        if(self.camera != None and self.camera.isOpened()):
            QMessageBox.critical(self,'错误','摄像头已开启，无需开始检测')

        '''每次在进行开始检测之前，先把checkBox_paramWindow_map中可能打开的窗口全部关闭'''
        self.clear_paramWindows_All()

        '''获取复选框勾选的信息'''
        checkboxs_list = []
        # 是否选择眨眼检测
        if(self.checkBox.isChecked()):
            self.choices['blink'] = True; checkboxs_list.append('blink')
            self.blinkParam_Window = BlinkParamWindow(self.cfg)
            self.checkBox_paramWindow_map['blink'] = self.blinkParam_Window
        else:
            self.choices['blink'] = False
        # 是否选择哈欠检测
        if (self.checkBox_2.isChecked()):
            self.choices['yawn'] = True; checkboxs_list.append('yawn')
            self.yawnParam_Window = YawnParamWindow(self.cfg)
            self.checkBox_paramWindow_map['yawn'] = self.yawnParam_Window
        else:
            self.choices['yawn'] = False
        # 是否选择点头检测
        if (self.checkBox_3.isChecked()):
            self.choices['head'] = True; checkboxs_list.append('head')
            self.nodParam_Window = NodParamWindow(self.cfg)
            self.checkBox_paramWindow_map['head'] = self.nodParam_Window
        else:
            self.choices['head'] = False
        # 是否选择疲劳检测
        # if (self.checkBox_5.isChecked()):
        #     self.choices['fatigue'] = True; checkboxs_list.append('fatigue')
        #     self.fatigueParam_Window = FatigueParamWindow(self.cfg)
        #     self.checkBox_paramWindow_map['fatigue'] = self.fatigueParam_Window
        # else:
        #     self.choices['fatigue'] = False
        print(self.choices)

        '''配置好关于参数子窗口的责任链，方便在当前子窗口通过下一步跳转至下一个配置子窗口'''
        # 设置checkboxs_list[-1]为终止配置界面：修改按钮和标志位
        if(len(checkboxs_list) != 0):
            cur_key = checkboxs_list[len(checkboxs_list) - 1]
            self.checkBox_paramWindow_map[cur_key].pushButton.setText('开始检测')
            self.checkBox_paramWindow_map[cur_key].last_ParamWindow_Flag = True
        # 根据指定复选框，用专门的子窗口完成算法参数配置（完成责任链的配置）
        i = len(checkboxs_list) - 1
        while (i > 0):
            cur_key = checkboxs_list[i]
            pre_key = checkboxs_list[i - 1]
            cur_window = self.checkBox_paramWindow_map[cur_key]
            pre_window = self.checkBox_paramWindow_map[pre_key]
            pre_window.nextParamWindow = cur_window
            cur_window.preParamWindow = pre_window
            #设置"上一步"按钮可见
            cur_window.set_preButton_Visible()
            i -= 1

        '''显示第一个参数配置子界面，并配置好算法处理器'''
        length = len(checkboxs_list)  #先不考虑frown
        # 是否选择疲劳检测
        if (self.checkBox_5.isChecked()):
            self.choices['fatigue'] = True; checkboxs_list.append('fatigue')
        else:
            self.choices['fatigue'] = False
        '''frown无需进行参数配置'''
        if (self.checkBox_4.isChecked()):
            self.choices['frown'] = True;checkboxs_list.append('frown')
        else:
            self.choices['frown'] = False

        if (len(checkboxs_list) == 1 and checkboxs_list[0] == 'fatigue'):  # 如果只选了疲劳检测，会提示需要先选择其他面部行为的检测
            QMessageBox.information(self, '提示', '需要先选择其他面部行为检测，再选择疲劳检测')
            return
        # 配置算法处理器
        self.detectorHandler = DetectHandler(self.cfg, checkboxs_list,self.scrfd_detector,self.landmark_detector,self.detectWindow)
        if(length != 0):
            self.checkBox_paramWindow_map[checkboxs_list[0]].handler = self.detectorHandler
            self.checkBox_paramWindow_map[checkboxs_list[0]].show()

        '''判断是否有选择复选框'''
        if(len(checkboxs_list) == 0):
            QMessageBox.information(self,'提示','请选择检测方案')
        # 判断是否只选择了frown检测,无需进行算法配置，直接通过detectorHandler配置算法
        elif(len(checkboxs_list) == 1 and checkboxs_list[0] == 'frown'):
            QMessageBox.information(self, "提示", "正在为你配置算法")
            self.detectorHandler.detect()

    # 结束检测（如果摄像头未开启，则会弹窗提示）
    def fatigue_detect_down(self):
        if (self.detectorHandler == None or self.detectorHandler.camera == None):  ##detectorHandler未装配或者摄像头未开启
            QMessageBox.critical(self, '错误', '摄像头未开启，无需结束检测')
        else:
            self.detectorHandler.detect_finished()
            self.detectorHandler = None

    def closeEvent(self, event):
        self.clear_paramWindows_All()  #清空所有的配置子窗口
        if (self.detectorHandler != None and self.detectorHandler.camera != None):  #detectorHandler已装配或者摄像头已开启
            self.detectorHandler.detect_finished()

        super().closeEvent(event)

    def clear_paramWindows_All(self):
        for key in self.checkBox_paramWindow_map.keys():
            param_window = self.checkBox_paramWindow_map[key]
            if(param_window != None):
                param_window.close()


if __name__ == '__main__':
    from PyQt5 import QtCore
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)#自适应分辨率

    app = QtWidgets.QApplication(sys.argv)
    window = DetectWindow()
    window.show()

    sys.exit(app.exec_())